﻿using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
using Object = UnityEngine.Object;

public static class ResourceHelper
{
#if UNITY_EDITOR
    public static bool IsAsync = false;
#else
    public static bool IsAsync = true;
#endif
    static Dictionary<string, Dictionary<string, Object>> resources = new Dictionary<string, Dictionary<string, Object>>();

    public static async Task<T> LoadAsync<T>(string assetName, string name) where T : Object
    {
        var dict = await LoadAssetBundleAsync(assetName);
        dict.TryGetValue(name.ToLower(), out var result);
        return result as T;
    }

    public static async Task<Object> LoadAsync(string assetName, string name)
    {
        var dict = await LoadAssetBundleAsync(assetName);
        dict.TryGetValue(name, out var result);
        return result;
    }

    public static async Task<Dictionary<string, Object>> LoadAssetBundleAsync(string assetName)
    {
        if (resources.TryGetValue(assetName, out var dict))
        {
            return dict;
        }

        dict = new Dictionary<string, Object>();
        resources[assetName] = dict;

        if (IsAsync)
        {
            var contnet = await ReadAsync(assetName);
            if (contnet == null)
            {
                Debug.LogWarning($"load {assetName} fail!");
                return dict;
            }
            var assetBundle = AssetBundle.LoadFromMemory(contnet);
            if (assetBundle == null)
            {
                Debug.LogError($"LoadFromMemory {assetName} fail!");
                return dict;
            }

            foreach (var name in assetBundle.GetAllAssetNames())
            {
                dict[Path.GetFileName(name)] = assetBundle.LoadAsset<Object>(name);
            }
        }
        else
        {
#if UNITY_EDITOR
            var paths = UnityEditor.AssetDatabase.GetAssetPathsFromAssetBundle(assetName);
            foreach (var path in paths)
            {
                dict[Path.GetFileName(path).ToLower()] = UnityEditor.AssetDatabase.LoadAssetAtPath<Object>(path);
            }
#endif
        }

        return dict;
    }

    public static async Task<byte[]> ReadAsync(string path)
    {
#if !UNITY_EDITOR
        var persistentDataPath = Path.Combine(Application.persistentDataPath, path);
        if (File.Exists(persistentDataPath))
            return await File.ReadAllBytesAsync(persistentDataPath);
#endif
        var streamingAssetsPath = Path.Combine(Application.streamingAssetsPath, path);
#if UNITY_EDITOR
        if (File.Exists(streamingAssetsPath))
            return await File.ReadAllBytesAsync(streamingAssetsPath);
#endif

        var request = UnityWebRequest.Get(streamingAssetsPath);
        request.SendWebRequest();
        while (!request.isDone)
        {
            await Task.Delay(1);
        }
        if (request.responseCode == 200)
        {
            if (string.IsNullOrEmpty(request.downloadHandler.error))
            {
                if (request.downloadHandler.data == null)
                {
                    if (File.Exists(streamingAssetsPath))
                    {
                        return await File.ReadAllBytesAsync(streamingAssetsPath);
                    }
                }
                var result = request.downloadHandler.data;
#if !UNITY_EDITOR
                var savePath = Path.Combine(Application.persistentDataPath, path);
                var dir = Path.GetDirectoryName(savePath);
                if(!string.IsNullOrEmpty(dir))
                {
                    if (!Directory.Exists(dir))
                        Directory.CreateDirectory(dir);
                }
                File.WriteAllBytesAsync(savePath, result);
                Debug.Log($"write to :{savePath}");
#endif
                return result;
            }
        }
        Debug.LogWarning($"[request]{path} [code]{request.responseCode} [error]{request.downloadHandler.error}");
        return default;
    }
}